#include"Log.h"
#include<iostream>
#include<sstream>
#include<map>
#include<functional>
#include <stdarg.h>
#include"config.h"
namespace Yizhi{
    const char * LogLevel::Tostring(LogLevel::Level level){
        switch (level)
        {

#define XX(name)    \
        case LogLevel::Level::name:     \
            return #name;
            break;

        XX(DEBUG);
        XX(INFO);
        XX(WARN);
        XX(ERROR);
        XX(FATAL);

#undef XX
        default:
            return "UNKNOW";
            break;
        }
        return "UNKNOW";
    }

    LogLevel::Level LogLevel::FormString(const std::string &str){
#define XX(str1,str2,level) \
        if(str==#str1||str==#str2)\
            return level;

        XX(debug,DEBUG,LogLevel::Level::DEBUG)
        XX(info,INFO,LogLevel::Level::INFO)
        XX(warn,WARN,LogLevel::Level::WARN)
        XX(fatal,FATAL,LogLevel::Level::FATAL)
        XX(error,ERROR,LogLevel::Level::ERROR)
#undef XX
        return LogLevel::Level::UNKNOW;
    }


    class LevelFormatItem: public LogFormatter::FormatItem{
        public:
        LevelFormatItem(const std::string &str=""){}
        void format(std::ostream &os,LogEvent::ptr event,LogLevel::Level level) override{
            os<<LogLevel::Tostring(level);
        }
    };
    class MessageFormatItem:public LogFormatter::FormatItem{
        public:
        MessageFormatItem(const std::string &str=""){}
        void format(std::ostream &os,LogEvent::ptr event,LogLevel::Level level) override {
            os<<event->getcontent();
        }
    
    };
    class ThreadFormatItem:public LogFormatter::FormatItem{
        public:
        ThreadFormatItem(const std::string &str=""){}
        void format(std::ostream &os,LogEvent::ptr event,LogLevel::Level level) override{
            os<<event->getThreadId();
        }
    };
    class StringFormatItem:public LogFormatter::FormatItem{
        public:
        StringFormatItem(const std::string &str=" ")
        :m_string(str){

        }
        void format(std::ostream &os,LogEvent::ptr event,LogLevel::Level level) override{
            os<<m_string;
        }
        private:
        std::string m_string;
    };
     class ThreadNameFormatItem:public LogFormatter::FormatItem{
        public:
        ThreadNameFormatItem(const std::string &str=""){}
        void format(std::ostream &os,LogEvent::ptr event,LogLevel::Level level) override{
            os<<event->getThreadName();
        }
    };
    class FiberFormatItem:public LogFormatter::FormatItem{
        public:
        FiberFormatItem(const std::string &str=""){}
        void format(std::ostream &os,LogEvent::ptr event,LogLevel::Level level) override{
            os<<event->getFiberId();
        }
    };
    class FileFormatItem: public LogFormatter::FormatItem{
        public:
        FileFormatItem(const std::string &str=""){}
        void format(std::ostream &os,LogEvent::ptr event,LogLevel::Level level) override{
            os<<event->getfilename();
        }
        
    };
    class ElapseFormatItem:public LogFormatter::FormatItem{
        public:
        ElapseFormatItem(const std::string &str=""){}
        void format(std::ostream &os,LogEvent::ptr event,LogLevel::Level level) override{
            os<<event->getelapse();
        }
    };
    class NameFormatItem:public LogFormatter::FormatItem{
        public:
        NameFormatItem(const std::string &str=""){}
        void format(std::ostream &os,LogEvent::ptr event,LogLevel::Level level) override{
            os<<event->getlogger();
        }
    };
    class DateTimeFormatItem:public LogFormatter::FormatItem {
        public:
        DateTimeFormatItem(const std::string &str="%Y-%m-%d %H:%M:%S")
        :m_string(str){
            if(m_string.empty()){
                m_string="%Y-%m-%d %H:%M:%S";
            }

        }
       
        void format(std::ostream &os,LogEvent::ptr event,LogLevel::Level level ) override{
            struct tm tm;
            time_t time=event->getTime();
            localtime_r(&time,&tm);
            char buf[64];
            strftime(buf,sizeof(buf),m_string.c_str(),&tm);
            os<<buf;

        }


        private:
        std::string m_string;
    };
    class NewLineFormatItem:public LogFormatter::FormatItem{
        public:
        NewLineFormatItem(const std::string &str=""){}
        void format(std::ostream &os,LogEvent::ptr event,LogLevel::Level level) override{
            os<<std::endl;
        }
    };

    class LineFormatItem:public LogFormatter::FormatItem{
        public:
        LineFormatItem(const std::string &str=""){}
        void format(std::ostream &os,LogEvent::ptr event,LogLevel::Level level) override{
            os<<event->getline();
        }
    };

    class TabFormatItem:public LogFormatter::FormatItem{
        public:
        TabFormatItem(const std::string &str=""){}
        void format(std::ostream &os,LogEvent::ptr event,LogLevel::Level level) override{
            os<<"\t";
        }
    };

    LogEvent::LogEvent(std::shared_ptr<Logger> logger
        ,LogLevel::Level level
        ,uint32_t pthreadid
        ,uint32_t fiberid
        ,uint64_t elapse
        ,uint32_t time
        ,int32_t m_line
        ,const char *filename
        ,const std::string& threadname )
    :m_logger(logger)
    ,m_level(level)
    ,m_pthreadId(pthreadid)
    ,m_fiberId(fiberid)
    ,m_elapse(elapse)
    ,m_time(time)
    ,m_line(m_line)
    ,m_filename(filename)
    ,m_threadName(threadname)
    {
    }
    void LogEvent::format(char *frm,...){
        va_list val;
        va_start(val,frm);
        format(frm,val);
        va_end(val);
    }
    void LogEvent::format(char *frm,va_list val){
        char *buf;
        int len =vasprintf(&buf,frm,val);
        if(len!=-1){
            m_ss<<std::string(buf,len);
            free(buf);
        }
        
    }
    LogEventWrap::LogEventWrap(LogEvent::ptr e):
    m_event(e){
    }
    LogEventWrap::~LogEventWrap(){
        m_event->getlogger()->log(m_event,m_event->getLevel());
    }
    std::stringstream& LogEventWrap::getSS(){
        return m_event->getss();
    }
    Logger::Logger(const std::string &name)
    :m_name(name)
    ,m_level(LogLevel::Level::DEBUG)
    {
    m_format.reset(new LogFormatter("%d%T[%p]%T%f%T%T%N%T%m%T%T%F%T%l%n"));
    }

    void Logger::log(LogEvent::ptr event,LogLevel::Level level){
        if(level>=m_level){
            MutexType::Lock lock(m_mutex);
            if(!m_appender.empty()){
                for(auto i:m_appender){
                    i->log(event,level);
                }
            }
            else{
                m_root->log(event,level);
            }
          
        }

    }


    
    void Logger::setformat(const std::string & fmt){
        MutexType::Lock lock(m_mutex);
        if(!fmt.empty()){
            m_format.reset(new LogFormatter(fmt));
        }
      
    }
    void Logger::Debug(LogEvent::ptr event){
        log(event,LogLevel::Level::DEBUG);
    }

    void Logger::Info(LogEvent::ptr event){
        log(event,LogLevel::Level::INFO);
    }

    void Logger::Warn(LogEvent::ptr event){
        log(event,LogLevel::Level::WARN);
    }

    void Logger::Error(LogEvent::ptr event){
        log(event,LogLevel::Level::ERROR);
    }

    void Logger::Fatal(LogEvent::ptr event){
        log(event,LogLevel::Level::FATAL);
    }
    void Logger::setAppender(LogAppender::ptr appender){
        MutexType::Lock lock(m_mutex);
        if(appender->getformatter()==nullptr){
            appender->setformat(m_format);
        }
        m_appender.push_back(appender);
    }
    void Logger::clearAppender(){
        MutexType::Lock lock(m_mutex);
        m_appender.clear();
    }
    void Logger::delAppender(LogAppender::ptr appender){
        MutexType::Lock lock(m_mutex);
        for(auto it=m_appender.begin();it!=m_appender.end();++it){
            if(*it==appender){
                m_appender.erase(it);
                break;
            }
        }
    }

    void LogAppender::setformat(LogFormatter::ptr fmt){
       Mutex::Lock lock(m_mutex);
        m_format=fmt;
    }
    void LogAppender::setformat(const std::string & fmt){
       Mutex::Lock lock(m_mutex);
        if(!fmt.empty()){
            m_format.reset(new LogFormatter(fmt));
        }
    }
    LogFormatter::ptr LogAppender::getformatter(){
       Mutex::Lock lock(m_mutex);
        return m_format;
    }
    

    void StdoutLogAppender::log(LogEvent::ptr event,LogLevel::Level level)
    {
        Mutex::Lock lock(m_mutex);
        if(level<=m_level){
          
            std::cout<<m_format->format(event,level);
        } 
    }
    FileLogAppender::FileLogAppender(const std::string &filename)
    :m_filename(filename){
        reopen();
    }
    bool FileLogAppender::reopen()
    {
        if(m_filestream){
            m_filestream.close();
        }
        m_filestream.open(m_filename,std::ios::app);
        return !! m_filestream;
    }
    void FileLogAppender::log(LogEvent::ptr event,LogLevel::Level level){
        Mutex::Lock lock(m_mutex);
        if(level<=m_level){
            
          m_filestream<<m_format->format(event,level);
        }
       
    }
    LogFormatter::LogFormatter(const std::string &str)
    :m_pattern(str){
        init();
    }

    void LogFormatter::init(){
        //解析 m_partten;
        //解析 [%p],%%p,%p %p{p}等字符串;
        std::vector<std::tuple<std::string,std::string,int>> vec;
        std::string nstr;
        for(size_t i=0;i<m_pattern.size();++i){
            if(m_pattern[i]!='%'){
                nstr.append(1,m_pattern[i]);
                continue;
            }
            if(i+1<m_pattern.size()&&m_pattern[i+1]=='%'){
                nstr.append(1,'%');
                break;
            }
            size_t n=i+1;
            size_t frmbegin;
            std::string str;
            std::string frm;
            int frmstaus=0;
            while(n<m_pattern.size()){
                if(!frmstaus&&(!isalpha(m_pattern[n])&&m_pattern[n]!='{'&&m_pattern[n]!='}')){
                    str=m_pattern.substr(i+1,n-i-1);
                    break;
                }
                if(frmstaus==0){
                    if(m_pattern[n]=='{'){
                        str=m_pattern.substr(i+1,n-i-1);
                        frmstaus=1;
                        n++;
                        frmbegin=n;
                        continue;
                    }
                }
                else if(frmstaus==1){
                    if(m_pattern[n]=='}'){
                        frmstaus=0;
                        frm=m_pattern.substr(frmbegin,n-frmbegin-1);
                        ++n;
                        break;
                    }
                }

                n++;
                if(str.empty()){
                    str=m_pattern.substr(i+1);
                }
            }
            if(frmstaus==0){
                if(!nstr.empty()){
                    vec.push_back(std::make_tuple(nstr,std::string(),0));
                    nstr.clear();
                }
                vec.push_back(std::make_tuple(str,frm,1));
                i=n-1;

            }
            else if(frmstaus==1){
                std::cout<<"<<error_pattern"+frm+">>"<<std::endl;
                vec.push_back(std::make_tuple("error_pattern",frm,0));
            }
        }
          static std::map<std::string,std::function<FormatItem::ptr(const std::string &str)>> s_format_items={
#define XX(str,C)   \
                    {#str,[](const std::string &frm){return FormatItem::ptr(new C(frm));}}

                    XX(p,LevelFormatItem),
                    XX(t,ThreadFormatItem),
                    XX(f,FileFormatItem),
                    XX(F,FiberFormatItem),
                    XX(d,DateTimeFormatItem),
                    XX(l,LineFormatItem),
                    XX(n,NewLineFormatItem),
                    XX(m,MessageFormatItem),
                    XX(r,ElapseFormatItem),
                    XX(T,TabFormatItem),
                    XX(N,ThreadNameFormatItem)
#undef XX
            };
         for(auto it:vec){
                if(std::get<2>(it)==0){
                    m_items.push_back(FormatItem::ptr(new StringFormatItem(std::get<0>(it))));
                }
                else {
                  auto t=s_format_items.find(std::get<0>(it));
                  if(t==s_format_items.end()){
                    m_items.push_back(FormatItem::ptr(new StringFormatItem("Error_pattern %"+std::get<0>(it))));
                  }else{
                    m_items.push_back(t->second(std::get<1>(it)));
                  }

                }
            }

    }
    std::string LogFormatter::format(LogEvent::ptr event,LogLevel::Level level){
        std::stringstream ss;
        for(auto &i:m_items){
            i->format(ss,event,level);
        }
        return ss.str();
    }

    LoggerManager::LoggerManager(){
        m_roots.reset(new Logger);
        m_roots->setAppender(LogAppender::ptr(new StdoutLogAppender));
    }
    Logger::ptr LoggerManager::GetLogger(const std::string &name){
        MutexType::Lock lock(m_mutex);
        auto it=m_logger.find(name);
        if(it!=m_logger.end()){
            return it->second;
        }
        lock.unlock();
        MutexType::Lock wlock(m_mutex);
        Logger::ptr logger(new Logger(name));
        logger->m_root=m_roots;
        m_logger[name]=logger;
        return logger;
    }

    void LoggerManager::delLogger(const std::string &name){
       MutexType::Lock lock(m_mutex);
        m_logger.erase(name);
    }

    struct LoggerAppenderDefine
    {
        std::string type;
        std::string formatter;
        std::string path;
        LogLevel::Level level;
        LoggerAppenderDefine()
        :type("")
        ,formatter("")
        ,path("")
        ,level(LogLevel::Level::UNKNOW){}

        bool operator==(const LoggerAppenderDefine & rhs) const{
            return type==rhs.type&&
                    formatter==rhs.formatter&&
                    path==rhs.path&&
                    level==rhs.level;
        }
       
    };

    struct LoggerDefine
    {
        std::vector<LoggerAppenderDefine> m_appenders;
        std::string name;
        LogLevel::Level level;
        std::string formatter;


        LoggerDefine()
        :m_appenders()
        ,name("")
        ,level(LogLevel::Level::UNKNOW)
        ,formatter("")
        
       {

        }
        bool operator<(const LoggerDefine& rhs) const{
            return name<rhs.name;
        }

        bool operator==(const LoggerDefine& rhs) const{
            return name==rhs.name&&
                   formatter==rhs.formatter&&
                   m_appenders==rhs.m_appenders&&
                   level==rhs.level;
        }
    };

    //序列化
    template<>
    class Lexical_cast<LoggerDefine,std::string>{
        public:
        std::string operator()(const LoggerDefine & val){
            YAML::Node node;
            std::stringstream ss;
            node["name"]=val.name;
            if(val.formatter!=""){
                node["formatter"]=val.formatter;
            }
            if(val.level!=LogLevel::Level::UNKNOW){
                node["level"]=LogLevel::Tostring(val.level);
            }
            for(auto it:val.m_appenders){
                YAML::Node t;
                if(it.type=="Stdout"){
                    t["type"]="Stdout";
                }
                else if(it.type=="File"){
                    t["type"]="File";
                    t["path"]=it.path;
                }
                if(it.level!=LogLevel::Level::UNKNOW){
                    t["level"]=LogLevel::Tostring(it.level);
                }
                if(it.formatter!=""){
                    t["formatter"]=it.formatter;
                }
                node.push_back(t);
            }
            ss.str("");
            ss<<node;
            return ss.str();
        }
    };

    //反序列化
    template<>
    class Lexical_cast<std::string,LoggerDefine>{
        public:
        LoggerDefine operator()(const std::string & val){
            LoggerDefine res;
            YAML::Node node=YAML::Load(val);
            if(!node["name"].IsDefined()){
                SERVER_LOG_ERROR(SERVER_LOG_ROOT())<<"name is nullptr";
                throw  std::logic_error("name is nullptr");
                
            }

            res.name=node["name"].as<std::string>();
            if(node["level"].IsDefined()){
                res.level=LogLevel::FormString(node["level"].as<std::string>());

            }
            if(node["formatter"].IsDefined()){
                res.formatter=node["formatter"].as<std::string>();
            }

            if(node["appender"].IsDefined()){
                LoggerAppenderDefine rt;
                for(size_t i=0;i<node["appender"].size();i++){
                    auto x=node["appender"][i];
                    std::string type=x["type"].IsDefined()?x["type"].as<std::string>():"";
                    rt.level=LogLevel::FormString(x["level"].as<std::string>());
                    if(type=="File"){
                        if(x["path"].IsDefined()){
                            rt.path=x["path"].as<std::string>();
                           
                        }
                        else{
                            continue;
                        }
                        rt.type=type;
                        if(x["formatter"].IsDefined()){
                            rt.formatter=x["formatter"].as<std::string>();
                        }
                    }
                    else if(type=="Stdout"){
                        if(x["formatter"].IsDefined()){
                            rt.formatter=x["formatter"].as<std::string>();
                        }
                         rt.type=type;
                    }else{
                        continue;
                    }
                   
                    SERVER_LOG_DEBUG(SERVER_LOG_ROOT())<<"Error";
                    res.m_appenders.push_back(rt);
                    SERVER_LOG_DEBUG(SERVER_LOG_ROOT())<<"success";
                }
            }
            return res;
        }
          
    };
    ConfigVar<std::set<LoggerDefine>>::ptr logger_defines=
    Config::Lookup("logs",std::set<LoggerDefine>(),"logger config");

    void LogInit::init(){
        logger_defines->addListener([](const std::set<LoggerDefine>& old_value
            ,const std::set<LoggerDefine>& new_value){
            SERVER_LOG_DEBUG(SERVER_LOG_ROOT())<<"on change cb";
            for(auto &i:new_value){
                if(i.name==""){
                    throw std::logic_error("name is null");
                }
                Logger::ptr logger;
                auto it=old_value.find(i);
                if(it!=old_value.end()){
                    if(*it==i){//说明当前logger和旧logger一支
                        continue;
                    }
                    logger=SERVER_LOG_NAME(i.name);
                }
                else{
                    logger=SERVER_LOG_NAME(i.name);
                }
                logger->setLevel(i.level);
                if(!i.formatter.empty()){
                    logger->setformat(i.formatter);
                }

                logger->clearAppender();
                for(auto &apd:i.m_appenders){
                    LogAppender::ptr rt;
                    if(apd.type=="File"){
                        if(apd.path.empty()){
                            continue;
                        }
                        rt.reset(new FileLogAppender(apd.path));
                    }
                    else if(apd.type=="Stdout"){
                        rt.reset(new StdoutLogAppender());
                    }
                    else {
                        continue;
                    }


                    if(!apd.formatter.empty()){
                        rt->setformat(apd.formatter);
                    }

                    rt->setLevel(apd.level);

                    logger->setAppender(rt);
                }
            }
            for(auto &i:old_value){
                auto it=new_value.find(i);
                if(it==new_value.end()){
                    Loggermgr::GetInstance()->delLogger(it->name);
                }
            }
        });
        
    }
    static LogInit __init__log;
}
